/*
 * Copyright (c) 2019, salesforce.com, inc.
 * All rights reserved.
 * Licensed under the BSD 3-Clause license.
 * For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
 */

export type Row = {
  [column: string]: string | number | boolean;
};

export type Column = {
  key: string;
  label: string;
};

const COLUMN_SEPARATOR = '  ';
const COLUMN_FILLER = ' ';
const HEADER_FILLER = '─';

const fillColumn = (label: string, width: number, filler: string, isLastCol: boolean): string =>
  `${label.padEnd(width, filler)}${isLastCol ? '' : COLUMN_SEPARATOR}`;

export const calculateMaxColumnWidths = (rows: Row[], cols: Column[]): Map<string, number> => {
  const maxColWidths = new Map<string, number>();
  cols.forEach(col => {
    rows.forEach(row => {
      const cell = String(row[col.key]);
      if (cell === undefined) {
        throw Error(`Row is missing the key ${col.key}`);
      }

      let maxColWidth = maxColWidths.get(col.key);
      if (maxColWidth === undefined) {
        maxColWidth = (col.label || col.key).length;
        maxColWidths.set(col.key, maxColWidth);
      }

      // if a cell is multiline, find the line that's the longest
      const longestLineWidth = cell
        .split('\n')
        .reduce((maxLine, line) => (line.length > maxLine.length ? line : maxLine)).length;
      if (longestLineWidth > maxColWidth) {
        maxColWidths.set(col.key, longestLineWidth);
      }
    });
  });
  return maxColWidths;
};

/** Creates a formatted table from rows and columns */
export const createTable = (rows: Row[], cols: Column[], title?: string): string => {
  const maxColWidths = calculateMaxColumnWidths(rows, cols);
  let table = title ? `=== ${title}` : '';

  let columnHeader = '';
  let headerSeparator = '';
  cols.forEach((col, index, arr) => {
    const width = maxColWidths.get(col.key);
    if (width) {
      const isLastCol = index === arr.length - 1;
      columnHeader += fillColumn(col.label || col.key, width, COLUMN_FILLER, isLastCol);
      headerSeparator += fillColumn('', width, HEADER_FILLER, isLastCol);
    }
  });

  table += columnHeader && headerSeparator ? `${title ? '\n' : ''}${columnHeader}\n${headerSeparator}\n` : '';

  rows.forEach(row => {
    let outputRow = '';
    cols.forEach((col, colIndex, colArr) => {
      const cell = String(row[col.key]);
      const isLastCol = colIndex === colArr.length - 1;
      const rowWidth = outputRow.length;
      cell.split('\n').forEach((line, lineIndex) => {
        const cellWidth = maxColWidths.get(col.key);
        if (cellWidth) {
          outputRow +=
            lineIndex === 0
              ? fillColumn(line, cellWidth, COLUMN_FILLER, isLastCol)
              : // If the cell is multiline, add an additional line to the table
                // and pad it to the beginning of the current column
                `\n${fillColumn('', rowWidth, COLUMN_FILLER, isLastCol)}${fillColumn(line, rowWidth, COLUMN_FILLER, isLastCol)}`;
        }
      });
    });
    table += `${outputRow}\n`;
  });

  return table;
};
